﻿#include <stack>
#include "Common.h"
#include "XmlFormat.h"

// xml 压缩
std::string XmlCompress(const char * pSrc)
{
    return RemoveSpaceAndEnter(pSrc);
}

// xml 格式化
std::string XmlFormat(const char *pXml, int nIndentSpaceNum)
{
    try
    {
        if (NULL == pXml)
        {
            return std::string("");
        }
        std::string strXmlCompress = RemoveSpaceAndEnter(pXml);
        int nQuotCnt = 0;       // 双引号个数
        bool bPush = true;      // 记录上一次是否是push操作
        size_t nStart = -1;     // '<'的位置
        size_t nStop = -1;      // '>'的位置
        std::stack<std::string> stackMatch; // 节点堆栈
        std::string strXmlFormat = "";      // 格式化后的串
        for (size_t i = 0; (i < strXmlCompress.length()) && (strXmlCompress[i] != 0); i++)
        {
            // 遇到 '"'
            if (strXmlCompress[i] == '\"')
            {
                nQuotCnt++;
            }
            bool bNeedSpace = true; // 是否需要空格
            // 双引号之间不做处理
            if (nQuotCnt % 2 != 0)
            {
                strXmlFormat += strXmlCompress[i];
                continue;
            }
            // 遇到 '<'
            else if (strXmlCompress[i] == '<')
            {
                strXmlFormat += strXmlCompress[i];
                nStart = i;
                continue;
            }
            // 遇到 '>'
            else if (strXmlCompress[i] == '>')
            {
                bNeedSpace = false;
                strXmlFormat += strXmlCompress[i];
                nStop = i;
                if (nStop == nStart)
                {
                    throw std::exception();
                }
                std::string strDic = strXmlCompress.substr(nStart);
                // xml 头标志，后面换行
                if (strXmlCompress[nStart + 1] == '?' && i > 0 && strXmlCompress[i - 1] == '?')
                {
                    strXmlFormat += "\r\n";
                }
                // 自闭合，“<XXX/>”的情况
                else if (i > 0 && strXmlCompress[i - 1] == '/')
                {
                    strXmlFormat += "\r\n";
                    bNeedSpace = true;
                    bPush = false;
                }
                // “</XXX>”的情况，标签闭合
                else if (nStart < strXmlCompress.length() - 1 && strXmlCompress[nStart + 1] == '/')
                {
                    strDic = strDic.substr(2, nStop - nStart - 2);
                    // 标签闭合了，将标签出栈
                    if (strDic.compare(stackMatch.top()) == 0)
                    {
                        stackMatch.pop();
                        // 上一次不是push，说明遇到了连续的闭合标签，移除已经添加的多余空格（减少缩进）
                        if (bPush == false)
                        {
                            // 标签长度+“</>”的长度+缩进空格个数
                            size_t nStartIdx = strXmlFormat.length() - strDic.length() - 3 - nIndentSpaceNum;
                            strXmlFormat.erase(nStartIdx, nIndentSpaceNum);
                        }
                        strXmlFormat += "\r\n";
                        bNeedSpace = true;
                        bPush = false;
                    }
                }
                else
                {
                    // 遇到“><”的情况，进行换行
                    if (i < strXmlCompress.length() - 1 && strXmlCompress[i + 1] == '<')
                    {
                        strXmlFormat += "\r\n";
                        bNeedSpace = true;
                    }
                    strDic = strDic.substr(1, nStop - nStart - 1);
                    size_t nPos = strDic.find(' ');
                    if (nPos != std::string::npos)
                    {
                        strDic = strDic.substr(0, nPos);
                    }
                    // 标签压栈
                    stackMatch.push(strDic);
                    bPush = true;
                }
                nStart = -1;
                nStop = -1;
            }
            else
            {
                // 默认情况直接赋值，不需要空格
                strXmlFormat += strXmlCompress[i];
                continue;
            }
            // 栈内有几个标签，就需要缩进几次
            for (size_t j = 0; bNeedSpace && (j < nIndentSpaceNum * stackMatch.size()); j++)
            {
                strXmlFormat += " ";
            }
        }
        return strXmlFormat;
    }
    catch (const std::exception&) {}
    return std::string(pXml);
}

